﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace WorkNodeFramework
{
    internal interface IFieldSet
    {
        FieldSetStatus DefaultStatus { get; set; }
        object this[string Name] { get; set; }
    }

    public abstract class FieldSet<T>:IEnumerable<string>, IFieldSet
        where T:FieldSet<T>
    {
        internal delegate object GetFieldHandler(FieldSet<T> owner, string Name);

        internal event GetFieldHandler GetField;

        List<FieldSetItem> items = new List<FieldSetItem>();
        FieldSetStatus defaultStatus = FieldSetStatus.New;

        protected FieldSet() {
        }

        public object this[string Name] {
            get {
                FieldSetItem find = Find(Name);
                if (find == null)
                {
                    if (GetField != null)
                    {
                        object o = GetField(this, Name);
                        this[Name] = o;
                        return o;
                    }
                    return null;
                }
                if (find.Value is DBNull)
                    return null;
                return find.Value;
            }
            set {
                FieldSetItem item = Find(Name);
                bool isNew = false;
                if (item == null)
                {
                    isNew = true;
                    item = new FieldSetItem();
                    item.Name = Name;
                }

                if (defaultStatus == FieldSetStatus.Ready)
                    item.Status = FieldSetStatus.Changed;

                if (value == null)
                    item.Value = DBNull.Value;
                else
                    item.Value = value;
                if (isNew)
                    items.Add(item);
            }
        }

        public bool IsNew {
            get {
                return defaultStatus == FieldSetStatus.New;
            }
        }

        public bool IsReady {
            get {
                return defaultStatus == FieldSetStatus.Ready;
            }
        }

        public bool IsModified {
            get {
                return defaultStatus == FieldSetStatus.Changed;
            }
        }

        FieldSetStatus IFieldSet.DefaultStatus
        {
            get {
                return defaultStatus;
            }
            set {
                if (defaultStatus != value)
                {
                    defaultStatus = value;
                    //=================
                    if (value == FieldSetStatus.New) {
                        foreach(FieldSetItem item in items)
                            if(item.Status == FieldSetStatus.Changed)
                                item.Status = FieldSetStatus.New;
                    }
                    else if (value == FieldSetStatus.Ready) {
                        foreach (FieldSetItem item in items)
                            item.Status = FieldSetStatus.Ready;
                    }
                }
            }
        }

        public string[] GetChangedItems() { 
            List<string> names = new List<string>();
            FieldSetStatus status = FieldSetStatus.New;
            if(defaultStatus==FieldSetStatus.Ready)
                status = FieldSetStatus.Changed;
            foreach (FieldSetItem item in items)
                if (item.Status == status)
                    names.Add(item.Name);
            return names.ToArray();
        }

        FieldSetItem Find(string Name) {
            FieldSetItem item = items.Find(delegate(FieldSetItem match) {
                return string.Compare(match.Name, Name) == 0;
            });
            return item;
        }

        public bool Remove(string Name) {
            FieldSetItem find = Find(Name);
            if (find != null)
                items.Remove(find);
            return find != null;
        }

        public void RemoveAll() {
            items.Clear();
        }

        #region IEnumerable<string> Members

        public IEnumerator<string> GetEnumerator()
        {
            foreach (FieldSetItem item in items)
                yield return item.Name;
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            foreach (FieldSetItem item in items)
                yield return item.Name;
        }

        #endregion

        internal void Save(string tableName)
        {
            Save(DataService.Provider, tableName);
        }

        internal void Save(DataProvider provider, string tableName) {
            ITable table = provider.GetTable(tableName);
            Save(table);
        }

        internal virtual void Save(ITable table)
        {
            if (defaultStatus == FieldSetStatus.New)
            {
                Insert ins = Insert(table);
                if (ins != null)
                    ins.Execute();
            }
            else if (defaultStatus == FieldSetStatus.Ready)
            {
                Update upd = Update(table);
                if (upd != null)
                    upd.Execute();
            }
            else if (defaultStatus == FieldSetStatus.Deleted)
            {
                Delete del = DeleteQuery(table);
                if (del != null)
                    del.Execute();
            }
        }

        public string BuildSaveStatement(ITable table, ParameterIndexCollection parameters)
        {
            if (defaultStatus == FieldSetStatus.New)
            {
                Insert ins = Insert(table);
                if (ins != null)
                    return table.Provider.BuildInsertQueryStatement(ins,parameters);
            }
            else if (defaultStatus == FieldSetStatus.Ready)
            {
                Update upd = Update(table);
                if (upd != null)
                    return table.Provider.BuildUpdateQueryStatement(upd,parameters);
            }
            else if (defaultStatus == FieldSetStatus.Deleted)
            {
                Delete del = DeleteQuery(table);
                if (del != null)
                    return table.Provider.BuildDeleteQueryStatement(del, parameters);
            }

            return string.Empty;
        }

        public IExecuteCommand GetSaveCommand(ITable table)
        {
            IExecuteCommand cmd = null;
            if (defaultStatus == FieldSetStatus.New)
                cmd = Insert(table);
            else if (defaultStatus == FieldSetStatus.Ready)
                cmd = Update(table);
            else if (defaultStatus == FieldSetStatus.Deleted)
                cmd = DeleteQuery(table);
            return cmd;
        }

        Delete DeleteQuery(ITable table)
        {
            List<FieldSetItem> keys = new List<FieldSetItem>();
            foreach (FieldSetItem item in items)
            {
                Column col = table[item.Name];
                if (col.IsPrimaryKey)
                    keys.Add(item);
            }
            if (keys.Count == 0)
                return null;
            Delete del = new Delete(table.Provider)
                .From(table);
            foreach (FieldSetItem item in keys)
                del.Where(WHERE.DO(table[item.Name]).IsEqualsTo(item.Value));
            return del;
        }

        Update Update(ITable table) {
            bool needUpdate = false;
            Update update = new Update(table);
            List<FieldSetItem> keys = new List<FieldSetItem>();
            foreach (FieldSetItem item in items)
            {
                Column col = table[item.Name];
                if (!col.IsPrimaryKey && item.Status == FieldSetStatus.Changed)
                {
                    update.Set(table[item.Name], item.Value);
                    needUpdate = true;
                }
                else if (col.IsPrimaryKey)
                    keys.Add(item);
            }

            if (keys.Count == 0 || !needUpdate)
                return null;
            foreach (FieldSetItem item in keys)
                update.Where(WHERE.DO(table[item.Name]).IsEqualsTo(item.Value));
            return update;
        }

        Insert Insert(ITable table) {
            bool needInsert = false;
            Insert insert = new Insert(table);
            foreach (FieldSetItem item in items)
                if (item.Status == FieldSetStatus.New)
                {
                    insert.Set(table[item.Name], item.Value);
                    needInsert = true;
                }
            if (!needInsert)
                return null;
            return insert;
        }
    }

    internal class FieldSetItem {
        public string Name { get; set; }
        public object Value { get; set; }

        FieldSetStatus status = FieldSetStatus.New;
        public FieldSetStatus Status {
            get {
                return status;
            }
            set {
                status = value;
            }
        }
    }

    public enum FieldSetStatus { 
        Ready,
        New,
        Changed,
        Deleted
    }
}
